package com.heima.search.service.impl;

import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.service.ArticleSearchService;
import com.heima.search.utils.ElasticSearchTemplate;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {
    
    // 0. 导入ElasticSearchTemplate
    @Autowired
    private ElasticSearchTemplate elasticSearchTemplate;

    @Override
    public ResponseResult search(UserSearchDto dto) throws IOException {
        // 目标：到ElasticSearch中搜索
        // 搜索条件包含：title,content中进行关键词搜索,publishTime进行范围搜索,根据publishTime进行降序排序

        // 1. 入参判断

        // 搜索框里面 输入的关键词
        String searchWords = dto.getSearchWords();
        Long minBehotTime = dto.getMinBehotTime();

        // 2. 使用关键字查询 getStringQueryBuilder
        // 针对title,content两个字段
        // 参数一：关键字
        // 参数二：连接条件
        // 参数三：字段（可变参数）
        QueryStringQueryBuilder stringQueryBuilder = elasticSearchTemplate.getStringQueryBuilder(searchWords, Operator.OR, "title", "content");

        // 3. 时间范围查询 getRangeBuilder
        // publishTime 字段 lt,gt
        // 参数一：在哪个字段上进行范围查询
        // 参数二：范围查找条件
        Map<String, Object> map = new HashMap<>();
        // < miniBeHotTime
        map.put("lt", minBehotTime);
        RangeQueryBuilder rangeQueryBuilder = elasticSearchTemplate.getRangeBuilder("publishTime", map);

        // 4. 布尔查询（支持多字段查询）getBoolQueryBuilder
        // 将关键字和范围查询组合查询，先must，再filter
        Map<String, AbstractQueryBuilder> map1 = new HashMap<>();
        // key: 连接条件
        // value: 要连接查询
        // 1. 先使用must把数据库中所有符合条件的数据查出来
        // 2. 再用filter条件去过滤上一步查出来的数据
        map1.put("must", stringQueryBuilder);
        map1.put("filter", rangeQueryBuilder);

        BoolQueryBuilder boolQueryBuilder = elasticSearchTemplate.getBoolQueryBuilder(map1);

        // 5. 执行查询 search
        // 参数一：到 app_info_article 映射里面查询数据
        // 参数二：条件查询 -> 先放空
        // 参数三：高亮设置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        // 参数四：页码
        // 参数五：每页显示条数
        // 参数六：排序字段
        // 参数七：排序方式
        SearchResponse search = elasticSearchTemplate.search(
                "app_info_article",
                boolQueryBuilder,
                highlightBuilder,
                dto.getPageNum(),
                dto.getPageSize(),
                "publishTime",
                SortOrder.DESC);

        List<Map<String, Object>> list = new ArrayList<>();
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            if(hit == null) {
                continue;
            }

            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            // 获得的sourceAsMap 和 最终要返回的结果相比之后，有两个字段有差异 h_title, staticUrl

            Map<String, Object> rs = new HashMap<>();
            rs.put("layout", sourceAsMap.get("layout"));
            rs.put("publishTime", sourceAsMap.get("publishTime"));
            rs.put("images", sourceAsMap.get("images"));
            rs.put("authorName", sourceAsMap.get("authorName"));
            rs.put("h_title", sourceAsMap.get("title"));
            rs.put("id", sourceAsMap.get("id"));
            rs.put("staticUrl", "");
            rs.put("authorId", sourceAsMap.get("authorId"));
            rs.put("title", sourceAsMap.get("title"));
            rs.put("content", sourceAsMap.get("content"));
            list.add(rs);
        }

        // 6. 提取结果 hits,hits,sourceAsMap
        
        // 7. 返回结果
        return ResponseResult.okResult(list);
    }
}